/*
 * File Name: StringUtil.java
 *------------------------------------------------------------------------------
 * Copyright 2012 NCR Corporation
 *------------------------------------------------------------------------------
 * All revision information is updated automatically from source code control
 * change records - please do not manually edit.
 *------------------------------------------------------------------------------
 *
 * $LastChangedRevision:: 1601                                     $
 * $LastChangedBy:: rb250096                                       $
 * $LastChangedDate:: 2012-02-22 17:11:39 -0500 (Wed, 22 Feb 2012) $
 */

package com.ncr.nep.dashboardcore.utils.client;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Utility class containing miscellaneous string manipulation methods.
 *
 * @author rb250096
 * @version $Revision: 1601 $
 */
public class StringUtil {

    /**
     * Concatenates an array of strings into a single string delimited by the
     * specified delimiter.
     *
     * @param delimiter The delimiter to put between values.
     * @param values 0 or more Objects to concatenate into the final string.
     *               If a value other than a String is encountered, it will
     *               be converted to a String via its {@link Object#toString()}
     *               method.
     *
     * @return The joined string.  If no values are specified, an empty string
     * will be returned.
     */
    public static String join(String delimiter, String ...values) {
        return join(delimiter, Arrays.asList(values));
    }

    /**
     * Concatenates a list of strings into a single string delimited by the
     * specified delimiter.
     *
     * @param delimiter The delimiter to put between values.
     * @param values List containing 0 or more Objects to concatenate into the
     *               final string. If a value other than a String is
     *               encountered, it will be converted to a String via its
     *               {@link Object#toString()} method.
     *
     * @return The joined string.  If no values are specified, an empty string
     * will be returned.
     */
    public static String join(String delimiter, List<?> values) {
        if(values == null || values.size() == 0 || delimiter == null) {
            return "";
        }

        StringBuilder s = new StringBuilder();
        for(int i = 0, l = values.size(); i < l; i++) {
            Object value = values.get(i);
            if(value != null) {
                s.append(value.toString());
                if(i < l - 1) {
                    s.append(delimiter);
                }
            }
        }
        
        String r = s.toString();
        if(r.endsWith(delimiter)) {
            r = r.substring(0, r.lastIndexOf(delimiter));
        }
        return r;
    }

    /**
     * Concatenates string key/value pairs into a single string
     * delimited by the specified delimiters.
     *
     * <p>Here is an example:</p>
     * <pre>
     * {@code
     *
     * String args = StringUtil.joinMap("&", "=", "a", 1, "b", 2, "c", 3);
     * System.out.println(args); // a=1&b=2&c=3
     * }
     * </pre>
     *
     * @param delimiter The delimiter to put between pairs.
     * @param keyValueDelimiter The delimiter to put between keys and values.
     * @param values 0 or more pairs to concatenate into the final string. Keys
     *               and values are specified in order.
     *               If a value other than a String is encountered, it will
     *               be converted to a String via its {@link Object#toString()}
     *               method.
     *
     * @return The joined string.  If no values are specified, an empty string
     * will be returned.
     */
    public static String joinMap(String delimiter,
                                 String keyValueDelimiter,
                                 Object ...values) {
        return joinMap(delimiter, keyValueDelimiter, MapUtil.newMap(values));
    }

    /**
     * Concatenates a map of string key/value pairs into a single string
     * delimited by the specified delimiters.
     *
     * @param delimiter The delimiter to put between pairs.
     * @param keyValueDelimiter The delimiter to put between keys and values.
     * @param values 0 or more pairs to concatenate into the final string.
     *               If a value other than a String is encountered, it will
     *               be converted to a String via its {@link Object#toString()}
     *               method.
     *
     * @return The joined string.  If no values are specified, an empty string
     * will be returned.
     */
    public static String joinMap(String delimiter, 
                              String keyValueDelimiter, 
                              Map<?, ?> values) {
        if(values == null || values.size() == 0 || delimiter == null ||
                keyValueDelimiter == null) {
            return "";
        }

        StringBuilder s = new StringBuilder();
        int i = 0;
        int l = values.size() - 1;
        
        for(Object key : values.keySet()) {
            if(key == null) {
                continue;
            }
            
            Object value = values.get(key);
            if(value != null) {
                s.append(key.toString());
                s.append(keyValueDelimiter);
                s.append(value.toString());
                
                if(i < l) {
                    s.append(delimiter);
                }
            }
            
            i += 1;
        }

        String r = s.toString();
        if(r.endsWith(delimiter)) {
            r = r.substring(0, r.lastIndexOf(delimiter));
        }
        return r;
    }

    /**
     * Substitutes <code>"{n}"</code> tokens found in the source string with
     * values provided in args into the source string.
     *
     * <p>
     *     The source string specifies 0 or more <code>"{n}"</code> tokens
     *     where <code>n</code> is the index of the argument to replace the
     *     token with.
     * </p>
     *
     * <p>Example:</p>
     * <pre>
     * {@code
     *
     * String s = StringUtil.substitute("First name: {0}, Last name: {1}", "John", "Smith");
     * System.out.println(s); // prints "First name: John, Last Name: Smith"
     * }
     * </pre>
     *
     * @param source The source string to substitute values into.
     * @param args 0 or more objects to substitute into the source string. Each
     *             object is substituted in based on its index in args.  If a
     *             value is not a String, it will be converted to one via its
     *             toString() method.
     *
     * @return The generated string with the values from args substituted into
     * the source.
     */
    public static String substitute(String source, Object ...args) {
        if(source == null) return "";

        String s = source;
        for(int i = 0, l = args.length; i < l; i++) {
            String token = "\\{" + i + "\\}";
            if(args[i] == null) continue;
            s = s.replaceAll(token, args[i].toString());
        }
        return s;
    }

    /**
     * Substitutes <code>"{n}"</code> tokens found in the source string with
     * values provided in args into the source string.
     *
     * <p>
     *     The source string specifies 0 or more <code>"{n}"</code> tokens
     *     where <code>n</code> is the key of the argument to replace the
     *     token with.
     * </p>
     *
     * <p>Example:</p>
     * <pre>
     * {@code
     *
     * Map<String, String> values = new HashMap<String, String>();
     * values.put("first", "John");
     * values.put("last", "Smith");
     * String s = StringUtil.substitute("First name: {first}, Last name: {last}", 
     *     values);
     * System.out.println(s); // prints "First name: John, Last Name: Smith"
     * }
     * </pre>
     *
     * @param source The source string to substitute values into.
     * @param args Map of key/value pairs to substitute into the source string.
     *             In order to substitute into the source string, the key in
     *             the map must be equal to a token's key.  If a value is not
     *             a String, it will be converted to one via its toString()
     *             method.
     *
     * @return The generated string with the values from args substituted into
     * the source.
     */
    public static String substitute(String source, Map<String, ?> args) {
        if(source == null) return "";

        String s = source;
        for(String key : args.keySet()) {
            String token = "\\{" + key + "\\}";
            Object value = args.get(key);
            if(value == null) continue;
            s = s.replaceAll(token, value.toString());
        }
        return s;        
    }

    /**
     * Substitutes <code>"{n}"</code> tokens found in the source string with
     * values provided in args into the source string.  This is a shorthand
     * for the {@link #substitute(String, java.util.Map)} method.
     *
     * <p>
     *     The source string specifies 0 or more <code>"{n}"</code> tokens
     *     where <code>n</code> is the key of the argument to replace the
     *     token with.
     * </p>
     *
     * <p>Example:</p>
     * <pre>
     * {@code
     *
     * String s = StringUtil.substitute("First name: {first}, Last name: {last}",
     *     "first", "John", "last", "Smith");
     * System.out.println(s); // prints "First name: John, Last Name: Smith"
     * }
     * </pre>
     *
     * @param source The source string to substitute values into.
     * @param args Array of key/value pairs to substitute into the source
     *             string.  Each key/value pair is specified as two arguments
     *             in the array following each other. If a value is not
     *             a String, it will be converted to one via its toString()
     *             method.
     *
     * @return The generated string with the values from args substituted into
     * the source.
     */
    public static String substituteNamed(String source, Object... args) {
        Map<Object, Object> map = MapUtil.newMap(args);
        Map<String, Object> rmap = new HashMap<String, Object>();
        for(Object key : map.keySet()) {
            rmap.put(key.toString(), map.get(key));
        }
        return substitute(source, rmap);
    }
}
